package com.upb.webserver.order;

import cn.hutool.core.collection.CollectionUtil;
import com.upb.webserver.account.AccountService;
import com.upb.webserver.application.ApplicationManager;
import com.upb.webserver.applicationorg.ApplicationOrgService;
import com.upb.webserver.chain.ChainManager;
import com.upb.webserver.chain.DeleteChainService;
import com.upb.webserver.chainhost.ChainHostManager;
import com.upb.webserver.chainorg.ChainOrgService;
import com.upb.webserver.common.constant.Constant;
import com.upb.webserver.common.enums.*;
import com.upb.webserver.common.exception.OperationException;
import com.upb.webserver.common.exception.WebServerException;
import com.upb.webserver.common.pojo.account.AccountCommon;
import com.upb.webserver.common.pojo.base.BasePageResponse;
import com.upb.webserver.common.pojo.base.BaseResponse;
import com.upb.webserver.common.pojo.host.RspHostVO;
import com.upb.webserver.common.pojo.node.RspNodeVO;
import com.upb.webserver.common.pojo.order.*;
import com.upb.webserver.common.pojo.org.RspOrgVO;
import com.upb.webserver.common.pojo.rest.fisco.chainmgr.resp.RespFront;
import com.upb.webserver.common.properties.WebServerProperties;
import com.upb.webserver.common.returncode.ConstantCode;
import com.upb.webserver.common.tools.JsonUtils;
import com.upb.webserver.common.tools.MybatisExampleTools;
import com.upb.webserver.company.CompanyDeveloperService;
import com.upb.webserver.dao.entity.*;
import com.upb.webserver.dao.mapper.*;
import com.upb.webserver.developerappmap.DeveloperAppMapService;
import com.upb.webserver.fisco.application.FiscoApplicationService;
import com.upb.webserver.fisco.chain.FiscoChainService;
import com.upb.webserver.host.HostManager;
import com.upb.webserver.limit.LimitConfig;
import com.upb.webserver.message.WorkOrderMessageService;
import com.upb.webserver.node.NodeService;
import com.upb.webserver.org.OrgManager;
import com.upb.webserver.rest.RespInitHost;
import com.upb.webserver.rest.fisco.FiscoChainMgrRestService;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.upb.webserver.common.constant.Constant.*;

/**
 * work order service
 * order type: OrderType.enums
 * order status: OrderStatus.enums
 */
@Log4j2
@Service
public class OrderService extends BaseOrderService {

    @Autowired
    private TbWorkOrderMapper workOrderMapper;
    @Autowired
    private AccountService accountService;
    @Autowired
    private WebServerProperties config;
    @Autowired
    private TbApplicationMapper applicationMapper;
    @Autowired
    private ApplicationManager applicationManager;
    @Autowired
    private TbChainMapper chainMapper;
    @Autowired
    private ChainManager chainManager;
    @Autowired
    private OrgManager orgManager;
    @Autowired
    private TbOrgMapper orgMapper;
    @Autowired
    private WorkOrderMessageService workOrderMessageService;
    @Autowired
    private FiscoChainService fiscoChainService;
    @Autowired
    private FiscoChainMgrRestService fiscoChainMgrRestService;
    @Autowired
    private FiscoApplicationService fiscoApplicationService;
    @Autowired
    private CompanyDeveloperService companyDeveloperService;
    @Autowired
    private TbDeveloperMapper developerMapper;
    @Autowired
    private TbCompanyMapper companyMapper;
    @Autowired
    private HostManager hostManager;
    @Autowired
    private WorkOrderManager workOrderManager;
    @Autowired
    private WebServerProperties webServerProperties;
    @Autowired
    private ChainHostManager chainHostManager;
    @Autowired
    private DeleteChainService deleteChainService;
    @Autowired
    private ChainOrgService chainOrgService;
    @Autowired
    private NodeService nodeService;
    @Autowired
    private ApplicationOrgService applicationOrgService;
    @Autowired
    private TbHostMapper hostMapper;
    @Autowired
    private DeveloperAppMapService developerAppMapService;

    /**
     * add order
     * todo check param not null: createBy, targetTable, targetId
     */
    public TbWorkOrder addWorkOrder(WorkOrderVO param) {
        log.info("start exec method [addWorkOrder]. param:{}", JsonUtils.objToString(param));

        //check detailJSon
//        this.checkDetailByType(param.getOrderType(), param.getDetail());

        // save work order
        TbWorkOrder workOrder = new TbWorkOrder();
        BeanUtils.copyProperties(param, workOrder);
        workOrder.setStatus(param.getOrderStatus().getValue());
        workOrder.setType(param.getOrderType().getValue());
        workOrder.setCreateFromPlatform(param.getCreateFromPlatform().getId());

        Date createTime = new Date();
        workOrder.setGmtCreate(createTime);
        workOrder.setGmtModified(createTime);

        workOrderMapper.insertSelective(workOrder);
        log.info("success exec method [workOrder]");
        return workOrderMapper.selectByPrimaryKey(workOrder.getId());
    }

    /**
     * update status/createBy/target/finish
     * todo check param not null: modifyBy, orderStatus, targetTable, targetId, if orderStatus is finished: closeBy
     */
    public void updateWorkOrder(TbWorkOrder workOrder) {
        log.info("start exec method [updateWorkOrder]. param:{}", JsonUtils.objToString(workOrder));

        //check id
        if (Objects.isNull(workOrder.getId())) {
            log.warn("fail exec method [updateWorkOrder] ,not found org by id:{}", workOrder.getId());
            throw new WebServerException(ConstantCode.INVALID_WORK_ORDER_ID);
        }

        //update
        workOrder.setGmtModified(new Date());
        workOrderMapper.updateByPrimaryKeyWithBLOBs(workOrder);
        log.info("success exec method [updateWorkOrder]");
    }


    /**
     * @param platform
     * @param currentAccountId
     * @param currentAccountName
     * @param param
     */
    @Transactional
    public ApplicationUnionOrder addWorkOrderOfNewApplication(byte platform, int currentAccountId, String currentAccountName, ReqAddWorkOrderOfNewApplicationVO param) throws OperationException {
        log.info("start exec method [addWorkOrderOfNewApplication]. platform:{} currentAccountId:{} currentAccountName:{} param:{}", platform, currentAccountId, currentAccountName, JsonUtils.objToString(param));

        //check chainId
        TbChain tbChain = chainMapper.selectByPrimaryKey(param.getChainId());
        if (Objects.isNull(tbChain)) {
            log.warn("unsuccessful exec method [addWorkOrderOfNewApplication]. not found chain record by chainId:{}", param.getChainId());
            throw new WebServerException(ConstantCode.INVALID_CHAIN_ID);
        }

        //check application name
        checkBeforeAddWorkOrderOfNewApp(tbChain.getId(), param);

        ChainTypeEnum chainTypeEnum;
        if ((chainTypeEnum = ChainTypeEnum.getById(tbChain.getChainType())) == null) {
            log.error("not support chainType:{}", tbChain.getChainType());
            throw new WebServerException(ConstantCode.CHAIN_TYPE_NOT_SUPPORT);
        }

        //OrderType
        OrderTypeEnum orderTypeEnum = null;
        if (ChainTypeEnum.FISCO == chainTypeEnum) {
            orderTypeEnum = OrderTypeEnum.NEW_APPLICATION_FISCO;
        } else {
            log.warn("unsuccessful exec method [addWorkOrderOfNewApplication]. not supportChainType:{}", chainTypeEnum.getId());
            throw new WebServerException(ConstantCode.CHAIN_TYPE_NOT_SUPPORT);
        }

        //set confirm result
        if (PlatformEnum.DEVELOPER.getId() == platform) {
            Integer currentCompanyId = companyDeveloperService.selectCompanyIdByDeveloperId(currentAccountId);
            param.getConfirmOrgList()
                    .stream()
                    .filter(co -> co.getCompanyId().equals(currentCompanyId))
                    .forEach(o -> o.setConfirmDeveloperId(currentAccountId).setConfirmFlag(true));
        } else {
            param.getConfirmOrgList().stream().forEach(co -> co.setConfirmFlag(true));
        }


        //add work order
        WorkOrderVO workOrderParam = new WorkOrderVO();
        workOrderParam.setDetail(JsonUtils.objToString(param));
        workOrderParam.setOrderStatus(OrderStatusEnum.APPLYING_BY_DEVELOPER);
        workOrderParam.setOrderType(orderTypeEnum);
        workOrderParam.setCreateFromPlatform(PlatformEnum.getById(platform));
        workOrderParam.setCreateBy(currentAccountId);
        workOrderParam.setModifyBy(currentAccountId);
        TbWorkOrder workOrderOnDb = addWorkOrder(workOrderParam);

        //check complete status
        if (hasAllOrgCompleted(workOrderOnDb)) {
            workOrderOnDb.setStatus(OrderStatusEnum.WAIT_FOR_MANAGER_HANDLE.getValue());
            updateWorkOrder(workOrderOnDb);
        } else if (PlatformEnum.DEVELOPER.getId() != platform) {
            throw new WebServerException(ConstantCode.REQUIRE_ALL_ORG_ADD_HOST);
        }

        //send message
        workOrderMessageService.sendAfterDeveloperAddWorkOrder(workOrderOnDb);


        //deploy
        ApplicationUnionOrder rsp = null;
        if (PlatformEnum.DEVELOPER.getId() != platform) {
            rsp = createApplication(workOrderOnDb.getId());
        }


        if (rsp == null) {
            rsp = new ApplicationUnionOrder();
            rsp.setWorkOrderId(workOrderOnDb.getId());
        }

        log.info("success exec method [addWorkOrderOfNewApplication].");
        return rsp;
    }

    /**
     * @param chainId
     * @param param
     */
    private void checkBeforeAddWorkOrderOfNewApp(int chainId, ReqAddWorkOrderOfNewApplicationVO param) {
        log.info("start exec method [checkBeforeAddWorkOrderOfNewApp]. newName:{}", JsonUtils.objToString(param));

        //check name
        TbApplicationExample example = MybatisExampleTools.initSampleExample(TbApplicationExample.class, new TbApplication(chainId, param.getName()));
        if (applicationMapper.countByExample(example) > 0) {
            log.warn("unsuccessful exec method [checkBeforeAddWorkOrderOfNewApp]. the chainId:{} applicationName:{} has bean use", chainId, param.getName());
            throw new WebServerException(ConstantCode.APPLICATION_NAME_HASH_BEAN_USE);
        }

        example = MybatisExampleTools.initSampleExample(TbApplicationExample.class, new TbApplication(chainId, param.getNameZh()));
        if (applicationMapper.countByExample(example) > 0) {
            log.warn("unsuccessful exec method [checkBeforeAddWorkOrderOfNewApp]. the chainId:{} applicationNameZh:{} has bean use", chainId, param.getNameZh());
            throw new WebServerException(ConstantCode.APPLICATION_NAME_HASH_BEAN_USE);
        }


        List<TbWorkOrder> workOrderList = workOrderManager.getAllWorkOrderOfNewApplication();
        for (TbWorkOrder workOrder : workOrderList) {
            ReqAddWorkOrderOfNewApplicationVO appDetailVO = JsonUtils.stringToObj(workOrder.getDetail(), ReqAddWorkOrderOfNewApplicationVO.class);
            if (appDetailVO.getName().equalsIgnoreCase(param.getName()) || appDetailVO.getNameZh().equalsIgnoreCase(param.getNameZh())) {
                log.warn("unsuccessful exec method [checkBeforeAddWorkOrderOfNewApp]. found appName:{} or appNameZh:{} on workOrder:{}", param.getName(), param.getNameZh(), workOrder.getId());
                throw new WebServerException(ConstantCode.APPLICATION_NAME_HASH_BEAN_USE);
            }
        }
    }


    /**
     * @param workOrderId
     */
    public ApplicationUnionOrder createApplication(int workOrderId) throws OperationException {
        log.info("start exec method [createApplication]. workOrderId:{}", workOrderId);

        //check workOrderId
        TbWorkOrder workOrder = workOrderManager.verifyWorkOrderId(workOrderId);
        //send message
        workOrderMessageService.sendBeforeManagerHandlingWorkOrder(workOrder);
        //check completed status
        super.verifyAllOrgHasCompleted(workOrder);
        //check status
        if (!SUPPORT_STATUS_BEFORE_DEPLOY.contains(workOrder.getStatus())) {
            log.warn("fail exec method [createApplication]. not support operation, currentStatus:{} support:{}", workOrder.getStatus(), JsonUtils.objToString(SUPPORT_STATUS_BEFORE_DEPLOY));
            throw new WebServerException(ConstantCode.NOT_SUPPORT_OPERATE);
        }

        //application
        ReqAddWorkOrderOfNewApplicationVO newApplicationVO = JsonUtils.stringToObj(workOrder.getDetail(), ReqAddWorkOrderOfNewApplicationVO.class);

        Integer applicationId = null;

        if (!ORDER_TYPE_LIST_NEW_APP.contains(workOrder.getType())) {
            log.warn("fail exec method [createApplication]. not support workOrderType:{}", workOrder.getType());
            throw new WebServerException(ConstantCode.NOT_SUPPORT_OPERATE);
        }

        try {
            if (workOrder.getType() == OrderTypeEnum.NEW_APPLICATION_FISCO.getValue()) {
                applicationId = fiscoApplicationService.createFiscoApplication(workOrder.getCreateFromPlatform(), workOrder.getCreateBy(), newApplicationVO);
            }
        } catch (WebServerException ex) {
            log.warn("fail exec method [createApplication] for WebServerException.", ex);
            workOrder.setStatus(OrderStatusEnum.HANDLE_FAIL_BY_MANAGER.getValue());
            updateWorkOrder(workOrder);
            throw OperationException.fromWebServerException(ex);
        } catch (Exception ex) {
            log.warn("fail exec method [createApplication]for Exception.", ex);
            workOrder.setStatus(OrderStatusEnum.HANDLE_FAIL_BY_MANAGER.getValue());
            updateWorkOrder(workOrder);
            throw new OperationException(ConstantCode.APPLICATION_DEPLOY_FAIL.msg(ex.getMessage()));
        }


        TbApplication application = null;
        Boolean isDeployRequestSuccess = true;
        OrderStatusEnum orderStatusEnum = OrderStatusEnum.HANDLE_SUCCESS_BY_MANAGER;
        if (Objects.isNull(applicationId) || (application = applicationMapper.selectByPrimaryKey(applicationId)) == null) {
            log.warn("fail exec method [createApplication]. applicationId is invalid:{}", applicationId);
            orderStatusEnum = OrderStatusEnum.HANDLE_FAIL_BY_MANAGER;
            isDeployRequestSuccess = false;
        }

        if (application != null && ChainOrAppStatusEnum.DEPLOY_FAIL.getId() == application.getCreateStatus()) {
            log.warn("fail exec method [createApplication]. application status is deploy fail");
            workOrder.setTargetTable(TableNameEnum.APPLICATION.getName());
            workOrder.setTargetId(applicationId);
            orderStatusEnum = OrderStatusEnum.HANDLE_FAIL_BY_MANAGER;
            isDeployRequestSuccess = false;
        }
        workOrder.setStatus(orderStatusEnum.getValue());
        workOrder.setTargetId(applicationId);
        workOrder.setTargetTable(TableNameEnum.APPLICATION.getName());
        updateWorkOrder(workOrder);

        log.info("isDeployRequestSuccess:{}", isDeployRequestSuccess);
        if (!isDeployRequestSuccess) {
            throw new OperationException(ConstantCode.APPLICATION_DEPLOY_NOT_SUCCESS);
        }

        ApplicationUnionOrder applicationUnionOrder = new ApplicationUnionOrder();
        applicationUnionOrder.setApplicationId(applicationId);
        applicationUnionOrder.setWorkOrderId(workOrderId);
        return applicationUnionOrder;
    }


    /**
     * @param platform
     * @param currentAccountId
     * @param accountName
     * @param workOrderId
     */
    public void updateWorkOrderOfNewApplication(byte platform, int currentAccountId, String accountName, int workOrderId) {
        log.info("start exec method [updateWorkOrderOfNewApplication]. workOrderId:{}", workOrderId);

        //check workOrderId
        TbWorkOrder workOrder = workOrderManager.verifyWorkOrderId(workOrderId);

        ReqAddWorkOrderOfNewApplicationVO applicationDetail = JsonUtils.stringToObj(workOrder.getDetail(), ReqAddWorkOrderOfNewApplicationVO.class);

        //set confirm result
        Integer currentCompanyId = companyDeveloperService.selectCompanyIdByDeveloperId(currentAccountId);
        applicationDetail.getConfirmOrgList()
                .stream()
                .filter(co -> co.getCompanyId().equals(currentCompanyId))
                .forEach(o -> o.setConfirmDeveloperId(currentAccountId).setConfirmFlag(true));

        workOrder.setModifyBy(currentAccountId);
        workOrder.setDetail(JsonUtils.objToString(applicationDetail));

        if (hasAllOrgCompleted(workOrder)) {
            workOrder.setStatus(OrderStatusEnum.WAIT_FOR_MANAGER_HANDLE.getValue());
        }

        updateWorkOrder(workOrder);

        //send message
        workOrderMessageService.sendAfterDeveloperUpdateWorkOrder(currentAccountId, accountName, workOrder);
        log.info("success exec method [updateWorkOrderOfNewApplication].");
    }

    @Transactional
    public BaseResponse addWorkOrderOfaddNode2App(AccountCommon accountCommon, ReqAddNode2AppVo reqAddNode2AppVo) {
        log.debug("start exec method [addWorkOrderOfaddNode2App]. reqAddNewNode2ChainVO:{}", JsonUtils.objToString(reqAddNode2AppVo));

        if (accountCommon.getPlatform() != PlatformEnum.DEV_OPS.getId() || !RoleTypeEnum.DEVOPS_ADMIN.getName().equalsIgnoreCase(accountCommon.getRoleName())) {
            throw new WebServerException(ConstantCode.NOT_SUPPORT_OPERATE);
        }

        //仅允许超级管理员操作。
        //不允许渠道管理员操作，新节点（机构）往往需要加入一条已存在的链，这条已存在的链未必属于这个渠道（未必是渠道通过API创建的）。
        //所以允许渠道管理员操作将产生越权风险。
        //TODO 增加相关校验：1、chainID、appID、NodeIDs是否合法

        //add work order
        WorkOrderVO workOrderParam = new WorkOrderVO();
        workOrderParam.setDetail(JsonUtils.objToString(reqAddNode2AppVo));
        workOrderParam.setOrderStatus(OrderStatusEnum.APPLYING_BY_DEVELOPER);
        workOrderParam.setOrderType(OrderTypeEnum.ADD_NODE_TO_APP);
        workOrderParam.setCreateFromPlatform(PlatformEnum.getById(accountCommon.getPlatform()));
        workOrderParam.setCreateBy(accountCommon.getId());
        workOrderParam.setModifyBy(accountCommon.getId());
        TbWorkOrder workOrderOnDb = addWorkOrder(workOrderParam);
        log.debug("exec method [addWorkOrder]. workOrderOnDb:{}", JsonUtils.objToString(workOrderOnDb));

        Set<String> ids = new HashSet<>(reqAddNode2AppVo.nodeIds);
        log.debug("exec method [fiscoChainMgrRestService.addSealerAsync]. req:{}", JsonUtils.objToString(ids));
        BaseResponse response = null;
        //尝试totalRetry次，每次间隔3秒
        int retryIndex = 0;
        final int totalRetry = 10;
        for (; retryIndex < totalRetry; retryIndex++) {
            try {
                response = fiscoChainMgrRestService.addSealerAsyncReturnValue(reqAddNode2AppVo.getChainId(), reqAddNode2AppVo.applicationId, ids);
            } catch (WebServerException e) {
                if (e.getRetCode().getCode() == 205007) {
                    log.debug("fiscoChainMgrRestService.addSealerAsyncReturnValue retry-->" + retryIndex);
                    try {
                        Thread.sleep(3000);
                    } catch (Exception e1) {
                        throw new WebServerException(ConstantCode.SYSTEM_EXCEPTION);
                    }
                    continue;
                } else {
                    log.debug("fiscoChainMgrRestService.addSealerAsyncReturnValue failed --> {}", e.getReturnData());
                    throw e;
                }
            } catch (Exception e3) {
                log.debug("fiscoChainMgrRestService.addSealerAsyncReturnValue failed --> {}", e3.getMessage());
                throw e3;
            }
        }
        if (retryIndex == totalRetry - 1) {
            throw new WebServerException(ConstantCode.SYSTEM_EXCEPTION.getCode(), "fiscoChainMgrRestService.addSealerAsyncReturnValue retry over 5 times");
        }
        if (response != null) {
            if (Constant.HTTP_SUCCESS_RESPONSE_CODE == response.getCode()) {
                List<RspNodeVO> nodeVOList = nodeService.selectFiscoNodeList(reqAddNode2AppVo.getChainId(), reqAddNode2AppVo.applicationId);
                Set<Integer> orgId = new HashSet<>();
                for (RspNodeVO nodeVO : nodeVOList) {
                    if (ids.contains(nodeVO.getNodeId())) {
                        List<TbOrg> tbOrgs = orgMapper.selectOrgByName(nodeVO.getBelongOrgName());
                        List<TbHost> tbHosts = hostMapper.selectHostByIp(nodeVO.getIp());
                        Set<Integer> orgIdSet = tbOrgs.stream().map(TbOrg::getId).collect(Collectors.toSet());
                        List<TbHost> hostList = tbHosts.stream().filter(h -> orgIdSet.contains(h.getOrgId())).collect(Collectors.toList());
                        if (CollectionUtil.isNotEmpty(hostList)) {
                            hostList.stream().forEach(host ->{
                                applicationOrgService.saveApplicationOrg(reqAddNode2AppVo.getChainId(), reqAddNode2AppVo.applicationId, host.getOrgId(), OrgTypeEnum.FISCO_ORG.getValue());
                                orgId.add(host.getOrgId());
                            });
                        }
                    }
                }
                if(CollectionUtil.isNotEmpty(orgId)){
                    for (Integer o : orgId){
                        TbOrg tbOrg = orgMapper.selectByPrimaryKey(o);
                        developerAppMapService.addMapOfDeveloperAndApplication(tbOrg.getDeveloperId(), reqAddNode2AppVo.applicationId);
                    }
                }
            }
        }
        workOrderOnDb.setStatus(OrderStatusEnum.HANDLE_SUCCESS_BY_MANAGER.getValue());
        updateWorkOrder(workOrderOnDb);
        return BaseResponse.initSuccess();
    }

    @Transactional
    public BaseResponse addWorkOrderOfNewNode(AccountCommon accountCommon, ReqAddNewNode2ChainVO reqAddNewNode2ChainVO) {

        log.debug("start exec method [addWorkOrderOfNewNode]. reqAddNewNode2ChainVO:{}", JsonUtils.objToString(reqAddNewNode2ChainVO));

        if (accountCommon.getPlatform() != PlatformEnum.DEV_OPS.getId() || !RoleTypeEnum.DEVOPS_ADMIN.getName().equalsIgnoreCase(accountCommon.getRoleName())) {
            throw new WebServerException(ConstantCode.NOT_SUPPORT_OPERATE);
        }

        //仅允许超级管理员操作。
        //不允许渠道管理员操作，新节点（机构）往往需要加入一条已存在的链，这条已存在的链未必属于这个渠道（未必是渠道通过API创建的）。
        //所以允许渠道管理员操作将产生越权风险。
        //TODO 增加相关校验：1、主机是否存在、有效
        //                  3、目前仅支持fisco链（获取链信息后一并校验）
        //                  4、目前支持持单个（？）

        //add work order
        WorkOrderVO workOrderParam = new WorkOrderVO();
        workOrderParam.setDetail(JsonUtils.objToString(reqAddNewNode2ChainVO));
        workOrderParam.setOrderStatus(OrderStatusEnum.APPLYING_BY_DEVELOPER);
        workOrderParam.setOrderType(OrderTypeEnum.ADD_NEW_NODE_TO_CHAIN);
        workOrderParam.setCreateFromPlatform(PlatformEnum.getById(accountCommon.getPlatform()));
        workOrderParam.setCreateBy(accountCommon.getId());
        workOrderParam.setModifyBy(accountCommon.getId());
        TbWorkOrder workOrderOnDb = addWorkOrder(workOrderParam);
        log.debug("exec method [addWorkOrder]. workOrderOnDb:{}", JsonUtils.objToString(workOrderOnDb));

        //call chainmanager
        BaseResponse response = fiscoChainService.addNode(reqAddNewNode2ChainVO);
        log.debug("exec method [fiscoChainService.addNode]. response:{}", JsonUtils.objToString(response));
        RespAddNodeVO respAddNodeVO = new RespAddNodeVO();
        respAddNodeVO.setChainId(reqAddNewNode2ChainVO.getChainId());
        respAddNodeVO.setWorkOrderId(workOrderOnDb.getId());


        if (response.isSuccess()) {
            //根据当前chainid和默认加入的群组id（即group=1）查询游离节点，并设置为group=1的共识节点
            //TODO chainmagaer里面group/consensus/list接口目前只能查到共识节点，查不到游离节点
            if (1 == 0) {
                Map<String, String> nodeTypeMap = fiscoChainMgrRestService.selectNodeConsensusTypeMap(reqAddNewNode2ChainVO.getChainId(), 1);
                log.debug("exec method [fiscoChainMgrRestService.selectNodeConsensusTypeMap]. response:{}", JsonUtils.objToString(nodeTypeMap));
                Set<String> ids = nodeTypeMap.keySet();
                for (String id : ids) {
                    if (!nodeTypeMap.get(id).equalsIgnoreCase(NODE_TYPE_REMOVE)) {
                        ids.remove(id);
                    }
                }
                log.debug("exec method [fiscoChainMgrRestService.addSealerAsync]. req:{}", JsonUtils.objToString(ids));
                fiscoChainMgrRestService.addSealerAsync(reqAddNewNode2ChainVO.getChainId(), 1, ids);
            }
            List<RespFront> nodeList = fiscoChainMgrRestService.selectFrontListByChainAndOrgId(reqAddNewNode2ChainVO.getChainId(), reqAddNewNode2ChainVO.getOrgId());
            Set<String> ids = nodeList.stream().map(front -> front.getNodeId()).collect(Collectors.toSet());
            log.debug("exec method [fiscoChainMgrRestService.addSealerAsync]. req:{}", JsonUtils.objToString(ids));
            //尝试totalRetry次，每次间隔3秒
            int retryIndex = 0;
            final int totalRetry = 10;
            for (; retryIndex < totalRetry; retryIndex++) {
                try {
                    fiscoChainMgrRestService.addSealerAsync(reqAddNewNode2ChainVO.getChainId(), 1, ids);
                } catch (WebServerException e) {
                    if (e.getRetCode().getCode() == 205007) {
                        log.debug("fiscoChainMgrRestService.addSealerAsyncWithResp retry-->" + retryIndex);
                        try {
                            Thread.sleep(3000);
                        } catch (Exception e1) {
                            throw new WebServerException(ConstantCode.SYSTEM_EXCEPTION);
                        }
                        continue;
                    } else {
                        log.debug("fiscoChainMgrRestService.addSealerAsyncWithResp failed --> {}", e.getReturnData());
                        throw e;
                    }
                } catch (Exception e3) {
                    log.debug("fiscoChainMgrRestService.addSealerAsyncWithResp failed --> {}", e3.getMessage());
                    throw e3;
                }
            }
            if (retryIndex == totalRetry - 1) {
                throw new WebServerException(ConstantCode.SYSTEM_EXCEPTION.getCode(), "fiscoChainMgrRestService.addSealerAsyncWithResp retry over 5 times");
            }
            chainOrgService.saveChainOrg(reqAddNewNode2ChainVO.getChainId(), reqAddNewNode2ChainVO.getOrgId(), OrgTypeEnum.FISCO_ORG);
            workOrderOnDb.setStatus(OrderStatusEnum.HANDLE_SUCCESS_BY_MANAGER.getValue());
            respAddNodeVO.setNodeIds(new ArrayList<>(ids));
        } else {
            workOrderOnDb.setStatus(OrderStatusEnum.HANDLE_FAIL_BY_MANAGER.getValue());
        }
        response.setData(respAddNodeVO);
        updateWorkOrder(workOrderOnDb);

        return response;

    }

    /**
     * @param platform
     * @param currentAccountId
     * @param currentAccountName
     * @param param
     */
    @Transactional
    public RspChainDeployVO addWorkOrderOfNewChain(byte platform, int currentAccountId, String currentAccountName, ReqAddWorkOrderOfNewChainVO param) {
        log.info("start exec method [addWorkOrderOfNewApplication]. platform:{} currentAccountId:{} currentAccountName:{} param:{}", platform, currentAccountId, currentAccountName, JsonUtils.objToString(param));

        //check name
        checkNameBeforeAddNewChain(param.getChainType(), param.getName());
        //check hostId  TODO
        if (!webServerProperties.getOpenDebug()) {
            log.info("is debug:{}", webServerProperties.getOpenDebug());
            checkHostIdBeforeAddNewChain(param);
        }

        ChainTypeEnum chainTypeEnum;
        if ((chainTypeEnum = ChainTypeEnum.getById(param.getChainType())) == null) {
            log.error("not support chainType:{}", param.getChainType());
            throw new WebServerException(ConstantCode.CHAIN_TYPE_NOT_SUPPORT);
        }

        OrderTypeEnum orderTypeEnum = OrderTypeEnum.NEW_CHAIN_FISCO;
        if (ChainTypeEnum.FABRIC == chainTypeEnum) {
            orderTypeEnum = OrderTypeEnum.NEW_CHAIN_FABRIC;
            param.getFabricOrderOrgList().stream().forEach(o -> o.setCompanyId(orgManager.getCompanyIdByOrgId(o.getOrgId())));
            param.getFabricPeerOrgList().stream().forEach(o -> o.setCompanyId(orgManager.getCompanyIdByOrgId(o.getOrgId())));
        } else if (ChainTypeEnum.FISCO == chainTypeEnum) {
            param.getFiscoOrgList().stream().forEach(o -> o.setCompanyId(orgManager.getCompanyIdByOrgId(o.getOrgId())));
        }

        //add work order
        WorkOrderVO workOrderParam = new WorkOrderVO();
        workOrderParam.setDetail(JsonUtils.objToString(param));
        workOrderParam.setOrderStatus(OrderStatusEnum.APPLYING_BY_DEVELOPER);
        workOrderParam.setOrderType(orderTypeEnum);
        workOrderParam.setCreateFromPlatform(PlatformEnum.getById(platform));
        workOrderParam.setCreateBy(currentAccountId);
        workOrderParam.setModifyBy(currentAccountId);
        TbWorkOrder workOrderOnDb = addWorkOrder(workOrderParam);

        //check complete status
        if (hasAllOrgCompleted(workOrderOnDb)) {
            workOrderOnDb.setStatus(OrderStatusEnum.WAIT_FOR_MANAGER_HANDLE.getValue());
            updateWorkOrder(workOrderOnDb);
        } else if (PlatformEnum.DEVELOPER.getId() != platform) {
            throw new WebServerException(ConstantCode.REQUIRE_ALL_ORG_ADD_HOST);
        }

        //send message
        workOrderMessageService.sendAfterDeveloperAddWorkOrder(workOrderOnDb);

        RspChainDeployVO rspChainDeployVO = null;
        if (PlatformEnum.DEVELOPER.getId() != platform) {
            rspChainDeployVO = chainDeploy(workOrderOnDb.getId(), DockerImageTypeEnum.MANUAL.getId());
        }
        if (rspChainDeployVO == null) {
            rspChainDeployVO = new RspChainDeployVO();
            rspChainDeployVO.setWorkOrderId(workOrderOnDb.getId());
        }

        log.info("success exec method [addWorkOrderOfNewChain].");
        return rspChainDeployVO;
    }

    /**
     * @param param
     */
    public void checkHostIdBeforeAddNewChain(ReqAddWorkOrderOfNewChainVO param) {
        log.info("start exec method [checkHostIdBeforeAddNewChain]. param:{}", JsonUtils.objToString(param));
        Set<Integer> inputHostSet = workOrderManager.getAllHostIdFromWorkOrderDetail(param);
        if (CollectionUtil.isEmpty(inputHostSet)) {
            log.warn("fail exec method [checkHostIdBeforeAddNewChain]. not found input host");
            throw new WebServerException(ConstantCode.NOT_FOUND_INPUT_HOST);
        }
        for (Integer hostId : inputHostSet) {
            //check from chain-host
            chainHostManager.verifyHostIdNotInUse(hostId);

            //check from workOrder
            Set<Integer> hostUseInWorkOrderSet = workOrderManager.getAllHostIdFromWorkOrderOfNewChain();

            //if (CollectionUtil.isNotEmpty(hostUseInWorkOrderSet) && hostUseInWorkOrderSet.contains(hostId)) {
            if ((CollectionUtil.isNotEmpty(hostUseInWorkOrderSet) && hostUseInWorkOrderSet.contains(hostId)) && false) {
                log.warn("fail exec method[checkHostIdBeforeAddNewChain]. found workOrder record by hostId:{}", hostId);
                TbHost tbHost = hostManager.verifyHostId(hostId);
                throw new WebServerException(ConstantCode.HOST_HAD_BEAN_IN_USE.msg(String.format("【id:%1d ip:%2s】 has bean in use", hostId, tbHost.getIp())));
            }
        }
    }

    /**
     * @param chainType
     * @param newName
     */
    public void checkNameBeforeAddNewChain(Byte chainType, String newName) {
        log.info("start exec method [checkBeforeAddNewChain]. chainType:{} newName:{}", chainType, newName);

        if (!webServerProperties.getSupportChainTypeList().contains(chainType)) {
            log.warn("fail exec method [checkBeforeAddNewChain]. not support chainType:{} ,just support:{}", chainType, JsonUtils.objToString(webServerProperties.getSupportChainTypeList()));
            throw new WebServerException(ConstantCode.CHAIN_TYPE_NOT_SUPPORT);
        }

        //check name
        if (chainMapper.selectByChainName(newName) != null) {
            log.warn("unsuccessful exec method [checkBeforeAddNewChain]. the name:{} has bean use", newName);
            throw new WebServerException(ConstantCode.CHAIN_NAME_HASH_BEAN_USE);
        }
        List<TbWorkOrder> workOrderList = workOrderManager.getAllWorkOrderOfNewChain();
        for (TbWorkOrder workOrder : workOrderList) {
            ReqAddWorkOrderOfNewChainVO chainDetailVO = JsonUtils.stringToObj(workOrder.getDetail(), ReqAddWorkOrderOfNewChainVO.class);
            if (chainDetailVO.getName() == newName) {
                log.warn("unsuccessful exec method [checkBeforeAddNewChain]. found chainName:{} on workOrder:{}", newName, workOrder.getId());
                throw new WebServerException(ConstantCode.CHAIN_NAME_HASH_BEAN_USE);
            }
        }
    }

    /**
     * @param platform
     * @param currentAccountId
     * @param currentAccountName
     * @param param
     */
    public void updateWorkOrderOfNewChain(byte platform, int currentAccountId, String currentAccountName,
                                          ReqUpdateWorkOrderOfNewChainVO param) {
        log.info("start exec method [updateWorkOrderOfNewChain]. platform:{} currentAccountId:{} currentAccountName:{} param:{}", platform, currentAccountId, currentAccountName, JsonUtils.objToString(param));
        //check workOrderId
        TbWorkOrder workOrder = workOrderManager.verifyWorkOrderId(param.getWorkOrderId());
        if (!ORDER_TYPE_LIST_NEW_CHAIN.contains(workOrder.getType())) {
            log.info("fail exec method [updateWorkOrderOfNewChain]. not support orderType:{}", workOrder.getType());
            throw new WebServerException(ConstantCode.NOT_SUPPORT_OPERATE);
        }

        ReqAddWorkOrderOfNewChainVO chainDetail = JsonUtils.objToJavaBean(workOrder.getDetail(), ReqAddWorkOrderOfNewChainVO.class);

        //set confirm result
        Integer currentCompanyId = companyDeveloperService.selectCompanyIdByDeveloperId(currentAccountId);
        Set<Integer> inputHostSet = new HashSet<>();
        if (OrderTypeEnum.NEW_CHAIN_FABRIC.getValue() == workOrder.getType() && !CollectionUtils.isEmpty(param.getFabricPeerOrgList())) {
            param.getFabricPeerOrgList().stream()
                    .filter(inputFabricPeerOrg -> currentCompanyId.equals(inputFabricPeerOrg.getCompanyId()))
                    .forEach(inputOrg -> {
                        chainDetail.getFabricPeerOrgList().stream()
                                .filter(oldPeerOrg -> oldPeerOrg.getOrgId().equals(inputOrg.getOrgId()))
                                .findFirst()
                                .ifPresent(oldOrg -> {
                                    inputOrg.getPeerNodeList().stream().forEach(n -> inputHostSet.add(n.getHostId()));
                                    BeanUtils.copyProperties(inputOrg, oldOrg);
                                });
                    });
        } else if (OrderTypeEnum.NEW_CHAIN_FISCO.getValue() == workOrder.getType()) {
            param.getFiscoOrgList().stream()
                    .filter(inputFiscoOrg -> currentCompanyId.equals(inputFiscoOrg.getCompanyId()))
                    .forEach(inputOrg -> {
                        chainDetail.getFiscoOrgList().stream()
                                .filter(oldPeerOrg -> oldPeerOrg.getOrgId().equals(inputOrg.getOrgId()))
                                .findFirst()
                                .ifPresent(oldOrg -> {
                                    inputOrg.getFiscoHostList().stream().forEach(n -> inputHostSet.add(n.getFiscoHostId()));
                                    BeanUtils.copyProperties(inputOrg, oldOrg);
                                });
                    });
        }

        //check host
        inputHostSet.stream().forEach(h -> chainHostManager.verifyHostIdNotInUse(h));
        Set<Integer> workOrderSetId = workOrderManager.getNewChainWorkOrderSetByHostSet(inputHostSet);

        //check from chain-host
        if (CollectionUtil.isNotEmpty(workOrderSetId)) {
            log.info("fail exec method [updateWorkOrderOfNewChain]. found workOrder record:{} by inputHostSet:{}", JsonUtils.objToString(workOrderSetId), JsonUtils.objToString(inputHostSet));
            //check from chain-host
            throw new WebServerException(ConstantCode.HOST_HAD_BEAN_IN_USE);
        }

        //update workOrder
        workOrder.setDetail(JsonUtils.objToString(chainDetail));
        workOrder.setModifyBy(currentAccountId);
        if (hasAllOrgCompleted(workOrder)) {
            workOrder.setStatus(OrderStatusEnum.WAIT_FOR_MANAGER_HANDLE.getValue());
        }

        updateWorkOrder(workOrder);

        //send message
        workOrderMessageService.sendAfterDeveloperUpdateWorkOrder(currentAccountId, currentAccountName, workOrder);
        log.info("success exec method [updateWorkOrderOfNewApplication].");
    }


    @Autowired
    private LimitConfig limitConfig;

    /**
     * @param workOrderId
     */
    public RspChainDeployVO chainDeploy(int workOrderId, Byte dockerImageType) {
        log.info("start exec method [chainDeploy]. workOrderId:{} dockerImageType:{}", workOrderId, dockerImageType);
        //check workOrderId
        TbWorkOrder workOrder = workOrderManager.verifyWorkOrderId(workOrderId);
        //send message
        workOrderMessageService.sendBeforeManagerHandlingWorkOrder(workOrder);
        //check completed status
        verifyAllOrgHasCompleted(workOrder);
        //check status
        if (!SUPPORT_STATUS_BEFORE_DEPLOY.contains(workOrder.getStatus())) {
            log.warn("fail exec method [chainDeploy]. not support operation, currentStatus:{} support:{}", workOrder.getStatus(), JsonUtils.objToString(SUPPORT_STATUS_BEFORE_DEPLOY));
            throw new WebServerException(ConstantCode.NOT_SUPPORT_OPERATE);
        }

        //check workOrderId
        ReqAddWorkOrderOfNewChainVO chainDetail = JsonUtils.objToJavaBean(workOrder.getDetail(), ReqAddWorkOrderOfNewChainVO.class);

        //save status
        workOrder.setGmtModified(new Date());
        workOrder.setStatus(OrderStatusEnum.HANDLING_BY_MANAGER.getValue());
        workOrderMapper.updateByPrimaryKey(workOrder);

        //deploy chain
        Integer chainId = null;
        try {
            if (workOrder.getType() == OrderTypeEnum.NEW_CHAIN_FISCO.getValue()) {
                chainId = fiscoChainService.fiscoDeploy(workOrder.getCreateFromPlatform(), workOrder.getCreateBy(), dockerImageType, chainDetail);
            }
        } catch (WebServerException ex) {
            log.warn("fail exec method [chainDeploy] for WebServerException.", ex);
            workOrder.setStatus(OrderStatusEnum.HANDLE_FAIL_BY_MANAGER.getValue());
            workOrder.setDescription(ex.getMessage());
            updateWorkOrder(workOrder);
            throw ex;
        } catch (Exception ex) {
            log.warn("fail exec method [chainDeploy]for Exception.", ex);
            workOrder.setStatus(OrderStatusEnum.HANDLE_FAIL_BY_MANAGER.getValue());
            workOrder.setDescription(ex.getMessage());
            updateWorkOrder(workOrder);
            throw ex;
        }

        TbChain chain = null;
        Boolean isDeployRequestSuccess = true;
        if (Objects.isNull(chainId) || (chain = chainMapper.selectByPrimaryKey(chainId)) == null) {
            log.warn("fail exec method [chainDeploy]. chainId:{} tbChain:{}", chainId, JsonUtils.objToString(chain));
            workOrder.setStatus(OrderStatusEnum.HANDLE_FAIL_BY_MANAGER.getValue());
            workOrder.setDescription("chain or chainId is null");
            isDeployRequestSuccess = false;
        } else {
            workOrder.setDescription("");
            workOrder.setTargetTable(TableNameEnum.CHAIN.getName());
            workOrder.setTargetId(chainId);
        }

        if (chain != null && ChainOrAppStatusEnum.DEPLOY_FAIL.getId() == chain.getCreateStatus()) {
            log.warn("fail exec method [chainDeploy]. chain status is deploy fail");
            workOrder.setStatus(OrderStatusEnum.HANDLE_FAIL_BY_MANAGER.getValue());
            workOrder.setDescription("chain remote deploy fail");
            isDeployRequestSuccess = false;
        }
        workOrder.setGmtModified(new Date());
        workOrderMapper.updateByPrimaryKey(workOrder);

        log.info("isDeployRequestSuccess:{}", isDeployRequestSuccess);
        if (!isDeployRequestSuccess) {
            throw new WebServerException(ConstantCode.CHAIN_DEPLOY_FAIL);
        }

        RspChainDeployVO result = new RspChainDeployVO(chainId, workOrderId);
        log.info("start exec method [chainDeploy]. workOrderId:{} result:{}", workOrderId, JsonUtils.objToString(result));
        return result;
    }


    /**
     * @param platform
     * @param accountId
     * @param orderType
     * @param statusList
     * @param pageNumber
     * @param pageSize
     * @return
     */
    public BasePageResponse queryWorkOrderByPage(int platform, int accountId, Byte orderType, List<Byte> statusList, int pageNumber, int pageSize) {
        log.info("start exec method [queryWorkOrderByPage]. pageNumber:{} pageSize:{} status:{}", pageNumber, pageSize, JsonUtils.objToString(statusList));
        BasePageResponse pageResponse = BasePageResponse.initialize();
        //query param
        Map<String, Object> inParam = new HashMap<>();
        inParam.put("status", statusList);

        TbWorkOrderExample example = MybatisExampleTools.initSamplePageExample(TbWorkOrderExample.class, pageNumber, pageSize, new TbWorkOrder(orderType, null, null), inParam);
        pageResponse.setTotalCount(workOrderMapper.countByExample(example));
        List<RspChainOrAppWorkOrderVO> rspList = new ArrayList<>();
        if (pageResponse.getTotalCount() > 0) {
            example.setOrderByClause(Constant.ORDER_BY_GMT_MODIFIED_DESC);

            //query
            List<TbWorkOrder> workOrderList = workOrderMapper.selectByExampleWithBLOBs(example);
            for (TbWorkOrder tbWorkOrder : workOrderList) {
                RspChainOrAppWorkOrderVO rsp = new RspChainOrAppWorkOrderVO();
                BeanUtils.copyProperties(tbWorkOrder, rsp);
                rsp.setOrderType(tbWorkOrder.getType());
                rsp.setCreatorOrg(getCreatorOrgName(tbWorkOrder));
//                rsp.setCreateByDeveloperId(tbWorkOrder.getCreateBy());
//                rsp.setCreateByDeveloperName(developerMapper.selectByPrimaryKey(tbWorkOrder.getCreateBy()).getAccount());
//                rsp.setCreateCompanyId(companyDeveloperService.selectCompanyIdByDeveloperId(tbWorkOrder.getCreateBy()));
//                rsp.setCreateCompanyName(companyDeveloperService.selectCompanyNameByDeveloperId(tbWorkOrder.getCreateBy()));
//                rsp.setCreateOrgId(getCreaterOrg(tbWorkOrder));
//                rsp.setCreateOrgName(orgMapper.selectNameByPrimaryKey(rsp.getCreateOrgId()));
                rsp.setHadSelfCompleted(getDeveloperCompleteStatus(platform, accountId, tbWorkOrder));
                rsp.setRelatedOrgListStr(getRelatedOrgListStr(tbWorkOrder));
                rsp.setName(getTargetNameOfWorkOrder(tbWorkOrder));
                rspList.add(rsp);
            }
            pageResponse.setData(rspList);
        }

        log.info("success exec method [queryDeveloperByPage]. result:{}", JsonUtils.objToString(pageResponse));
        return pageResponse;
    }


    /**
     * @param tbWorkOrder
     * @return
     */
    private String getTargetNameOfWorkOrder(TbWorkOrder tbWorkOrder) {
        log.debug("start exec method [getTargetNameOfWorkOrder]. tbWorkOrder:{}", JsonUtils.objToString(tbWorkOrder));

        Byte type = tbWorkOrder.getType();

        String name = null;
        if (ORDER_TYPE_LIST_NEW_CHAIN.contains(type)) {
            ReqAddWorkOrderOfNewChainVO chainDetailVO = JsonUtils.stringToObj(tbWorkOrder.getDetail(), ReqAddWorkOrderOfNewChainVO.class);
            name = chainDetailVO.getName();
        } else if (ORDER_TYPE_LIST_NEW_APP.contains(type)) {
            ReqAddWorkOrderOfNewApplicationVO appDetail = JsonUtils.stringToObj(tbWorkOrder.getDetail(), ReqAddWorkOrderOfNewApplicationVO.class);
            name = appDetail.getNameZh();
        } else if (OrderTypeEnum.JOIN_CROSS_CHAIN.getValue() == type) {
            ReqAddWorkOrderOfCrossChainVO orderDetail = JsonUtils.objToJavaBean(tbWorkOrder.getDetail(), ReqAddWorkOrderOfCrossChainVO.class);
            name = orderDetail.getCrossChainName();
        } else {
            log.warn("finish exec method [getTargetNameOfWorkOrder] ,not support workOrderType:{}", type);
            return null;
        }

        log.debug("finish exec method [getTargetNameOfWorkOrder]. name:{}", name);
        return name;
    }


    /**
     * @param workOrder
     * @return
     */
    private String getRelatedOrgListStr(TbWorkOrder workOrder) {
        log.info("start exec method [getRelatedOrgListStr]. workOrder:{}", JsonUtils.objToString(workOrder));

        Byte type = workOrder.getType();
        Set<Integer> orgIdSet = new HashSet<>();

        log.info("orderType:{}", type);
        if (type == OrderTypeEnum.NEW_CHAIN_FABRIC.getValue()) {
            ReqAddWorkOrderOfNewChainVO chainDetailVO = JsonUtils.stringToObj(workOrder.getDetail(), ReqAddWorkOrderOfNewChainVO.class);
            List<Integer> peerOrgIdList = chainDetailVO.getFabricPeerOrgList().stream()
                    .map(o -> o.getOrgId())
                    .collect(Collectors.toList());
            List<Integer> orderOrgIdList = chainDetailVO.getFabricOrderOrgList().stream()
                    .map(o -> o.getOrgId())
                    .collect(Collectors.toList());

            log.debug("peerOrgIdList:{} peerOrgIdList:{}", JsonUtils.objToString(peerOrgIdList), JsonUtils.objToString(orderOrgIdList));
            Stream.of(peerOrgIdList, orderOrgIdList).forEach(orgIdSet::addAll);
        } else if (type == OrderTypeEnum.NEW_CHAIN_FISCO.getValue()) {
            ReqAddWorkOrderOfNewChainVO chainDetailVO = JsonUtils.stringToObj(workOrder.getDetail(), ReqAddWorkOrderOfNewChainVO.class);
            List<Integer> orgIdList = chainDetailVO.getFiscoOrgList().stream()
                    .map(o -> o.getOrgId())
                    .collect(Collectors.toList());
            orgIdSet.addAll(orgIdList);
        } else if (type == OrderTypeEnum.NEW_APPLICATION_FABRIC.getValue() || type == OrderTypeEnum.NEW_APPLICATION_FISCO.getValue()) {
            ReqAddWorkOrderOfNewApplicationVO appDetail = JsonUtils.stringToObj(workOrder.getDetail(), ReqAddWorkOrderOfNewApplicationVO.class);
            List<Integer> orgIdList = appDetail.getConfirmOrgList().stream()
                    .map(o -> o.getOrgId())
                    .collect(Collectors.toList());
            orgIdSet.addAll(orgIdList);
        }

        log.debug("orgIdSet:{}", JsonUtils.objToString(orgIdSet));
        if (CollectionUtils.isEmpty(orgIdSet)) return "";
        List<Integer> orgIdList = new ArrayList<>(orgIdSet);
        List<TbOrg> orgList = orgMapper.listByOrgIdList(orgIdList);
        String orgNameListStr = orgList.stream().map(o -> o.getName()).collect(Collectors.joining(","));
        log.info("success exec method [getRelatedOrgListStr]. result:{}", orgNameListStr);
        return orgNameListStr;

    }


    /**
     * @param platform
     * @param developerId
     * @param workOrder
     * @return true: already complete  false: have not complete
     */
    public Boolean getDeveloperCompleteStatus(int platform, int developerId, TbWorkOrder workOrder) {
        log.info("start exec method [getDeveloperCompleteStatus]. platform:{} developerId:{} workOrder:{}", platform, developerId, JsonUtils.objToString(workOrder));

        if (platform != PlatformEnum.DEVELOPER.getId()) {
            return null;
        }

        Byte type = workOrder.getType();
        int createCompanyId = companyDeveloperService.selectCompanyIdByDeveloperId(developerId);
        if (type == OrderTypeEnum.NEW_CHAIN_FABRIC.getValue()) {
            ReqAddWorkOrderOfNewChainVO chainDetailVO = JsonUtils.stringToObj(workOrder.getDetail(), ReqAddWorkOrderOfNewChainVO.class);
            return chainDetailVO.getFabricPeerOrgList().stream()
                    .filter(fo -> fo.getCompanyId() == createCompanyId)
                    .findFirst()
                    .map(o -> o.getCaNode() != null &&
                            !CollectionUtils.isEmpty(o.getPeerNodeList()))
                    .orElse(false);
        } else if (type == OrderTypeEnum.NEW_CHAIN_FISCO.getValue()) {
            ReqAddWorkOrderOfNewChainVO chainDetailVO = JsonUtils.stringToObj(workOrder.getDetail(), ReqAddWorkOrderOfNewChainVO.class);
            return chainDetailVO.getFiscoOrgList().stream()
                    .filter(fo -> fo.getCompanyId() == createCompanyId)
                    .findFirst()
                    .map(o -> !CollectionUtils.isEmpty(o.getFiscoHostList())).orElse(false);
        } else if (type == OrderTypeEnum.NEW_APPLICATION_FABRIC.getValue() || type == OrderTypeEnum.NEW_APPLICATION_FISCO.getValue()) {
            ReqAddWorkOrderOfNewApplicationVO appDetail = JsonUtils.stringToObj(workOrder.getDetail(), ReqAddWorkOrderOfNewApplicationVO.class);
            return appDetail.getConfirmOrgList().stream()
                    .filter(fo -> fo.getCompanyId() == createCompanyId)
                    .findFirst()
                    .map(o -> o.getConfirmFlag())
                    .orElse(false);
        } else if (type == OrderTypeEnum.JOIN_CROSS_CHAIN.getValue()) {
            ReqAddWorkOrderOfCrossChainVO orderDetail = JsonUtils.objToJavaBean(workOrder.getDetail(), ReqAddWorkOrderOfCrossChainVO.class);

            return orderDetail.getApplicationOrgCollection().stream()
                    .filter(fo -> fo.getCompanyId() == createCompanyId)
                    .findFirst()
                    .map(o -> !CollectionUtils.isEmpty(o.getApplicationIdCollection()))
                    .orElse(false);
        }
        return false;
    }


    /**
     * @param platform
     * @param developerId
     * @param workOrderId
     * @return
     */
    public List<RspOrgVO> getSelfOrg(int platform, int developerId, int workOrderId) {
        log.info("start exec method [getSelfOrg]. platform:{} developerId:{} workOrderId:{}", platform, developerId, workOrderId);

        TbWorkOrder workOrder = workOrderManager.verifyWorkOrderId(workOrderId);

        List<RspOrgVO> rspOrgVOList = getSelfOrg(platform, developerId, workOrder);
        log.info("success exec method [getSelfOrg]. result:{}", JsonUtils.objToString(rspOrgVOList));
        return rspOrgVOList;
    }


    public List<RspOrgVO> getSelfOrg(int platform, int developerId, TbWorkOrder workOrder) {
        log.info("start exec method [getSelfOrg]. platform:{} developerId:{} workOrderId:{}", platform, developerId, workOrder.getId());

        if (PlatformEnum.DEVELOPER.getId() != platform) {
            log.warn("fail exec method [getSelfOrg]. not support platform:{}", platform);
            throw new WebServerException(ConstantCode.NOT_SUPPORT_OPERATE);
        }

        Integer createCompanyId = companyDeveloperService.selectCompanyIdByDeveloperId(developerId);
        Set<Integer> orgIdSet = new HashSet<>();
        Byte type = workOrder.getType();
        if (type == OrderTypeEnum.NEW_CHAIN_FABRIC.getValue()) {
            ReqAddWorkOrderOfNewChainVO chainDetailVO = JsonUtils.stringToObj(workOrder.getDetail(), ReqAddWorkOrderOfNewChainVO.class);
            //peer org
            chainDetailVO.getFabricPeerOrgList().stream()
                    .filter(fo -> fo.getCompanyId() == createCompanyId)
                    .forEach(o -> orgIdSet.add(o.getOrgId()));

            //order org
            chainDetailVO.getFabricOrderOrgList().stream()
                    .filter(fo -> createCompanyId.equals(fo.getCompanyId()))
                    .forEach(o -> orgIdSet.add(o.getOrgId()));
        } else if (type == OrderTypeEnum.NEW_CHAIN_FISCO.getValue()) {
            ReqAddWorkOrderOfNewChainVO chainDetailVO = JsonUtils.stringToObj(workOrder.getDetail(), ReqAddWorkOrderOfNewChainVO.class);
            chainDetailVO.getFiscoOrgList().stream()
                    .filter(fo -> createCompanyId.equals(fo.getCompanyId()))
                    .forEach(o -> orgIdSet.add(o.getOrgId()));
            ;
        } else if (type == OrderTypeEnum.NEW_APPLICATION_FABRIC.getValue() || type == OrderTypeEnum.NEW_APPLICATION_FISCO.getValue()) {
            ReqAddWorkOrderOfNewApplicationVO appDetail = JsonUtils.stringToObj(workOrder.getDetail(), ReqAddWorkOrderOfNewApplicationVO.class);
            appDetail.getConfirmOrgList().stream()
                    .filter(fo -> createCompanyId.equals(fo.getCompanyId()))
                    .forEach(o -> orgIdSet.add(o.getOrgId()));
        } else if (type == OrderTypeEnum.JOIN_CROSS_CHAIN.getValue()) {
            ReqAddWorkOrderOfCrossChainVO orderDetail = JsonUtils.objToJavaBean(workOrder.getDetail(), ReqAddWorkOrderOfCrossChainVO.class);

            orderDetail.getApplicationOrgCollection().stream()
                    .filter(fo -> fo.getCompanyId() == createCompanyId)
                    .forEach(o -> orgIdSet.add(o.getOrgId()));
        } else {
            log.error("fail exec method [getSelfOrg]. not support orderType:{}", type);
            return null;
        }

        if (CollectionUtil.isEmpty(orgIdSet)) {
            log.error("fail exec method [getSelfOrg]. orgIdList is empty");
            return null;
        }

        List<RspOrgVO> rspOrgVOList = new ArrayList<>(orgIdSet.size());
        orgIdSet.stream().forEach(oi -> {
            RspOrgVO rspOrgVO = new RspOrgVO();
            rspOrgVO.setId(oi);
            rspOrgVO.setName(orgMapper.selectNameByPrimaryKey(oi));
            rspOrgVO.setCompanyId(createCompanyId);
            rspOrgVOList.add(rspOrgVO);
        });

        log.info("success exec method [getSelfOrg]. result:{}", JsonUtils.objToString(rspOrgVOList));
        return rspOrgVOList;
    }


    /**
     * @param workOrderId
     * @return
     */
    public RspHeaderOfWorkOrderVO getHeaderOfWorkOrder(int workOrderId) {
        log.info("start exec method [getHeaderOfWorkOrder]. workOrderId:{}", workOrderId);
        TbWorkOrder workOrder = workOrderManager.verifyWorkOrderId(workOrderId);
        RspHeaderOfWorkOrderVO rsp = new RspHeaderOfWorkOrderVO();
        BeanUtils.copyProperties(workOrder, rsp);
        try {
            rsp.setCreateStatus(OrderStatusEnum.getByValue(workOrder.getStatus()).convertToChainOrAppStatusEnum().getId());
        } catch (Exception ex) {
            log.info("fail to convert create status", ex);
        }
        rsp.setOrderType(workOrder.getType());
        rsp.setCreatorOrg(super.getCreatorOrgName(workOrder));
        rsp.setCreatorAccountName(accountService.getAccountNameById(workOrder.getCreateFromPlatform(), workOrder.getCreateBy()));
//        rsp.setCreateByDeveloperId(workOrder.getCreateBy());
//        rsp.setCreateByDeveloperName(developerMapper.selectByPrimaryKey(workOrder.getCreateBy()).getAccount());
//        rsp.setCreateCompanyId(companyDeveloperService.selectCompanyIdByDeveloperId(workOrder.getCreateBy()));
//        rsp.setCreateCompanyName(companyDeveloperService.selectCompanyNameByDeveloperId(workOrder.getCreateBy()));
//        rsp.setCreateOrgId(getCreaterOrg(workOrder));
//        rsp.setCreateOrgName(orgMapper.selectNameByPrimaryKey(rsp.getCreateOrgId()));

        Byte type = workOrder.getType();
        if (ORDER_TYPE_LIST_NEW_CHAIN.contains(type)) {
            ReqAddWorkOrderOfNewChainVO newChainVO = JsonUtils.stringToObj(workOrder.getDetail(), ReqAddWorkOrderOfNewChainVO.class);
            rsp.setDescription(newChainVO.getDescription());
            rsp.setName(newChainVO.getName());
            rsp.setVersion(newChainVO.getVersion());
            rsp.setChainType(newChainVO.getChainType());
            rsp.setEncryptionId(newChainVO.getEncryptionId());
        } else if (ORDER_TYPE_LIST_NEW_APP.contains(type)) {
            ReqAddWorkOrderOfNewApplicationVO newAppVO = JsonUtils.stringToObj(workOrder.getDetail(), ReqAddWorkOrderOfNewApplicationVO.class);
            rsp.setDescription(newAppVO.getDescription());
            rsp.setName(newAppVO.getName());
            rsp.setBelongChainId(newAppVO.getChainId());
            rsp.setVersion(newAppVO.getVersion());
            rsp.setBelongChainName(chainMapper.selectNameByPrimaryKey(newAppVO.getChainId()));
            TbChain tbChain = chainManager.verifyChainId(newAppVO.getChainId());
            rsp.setChainType(tbChain.getChainType());
            rsp.setEncryptionId(tbChain.getEncryptionId());
        } else {
            log.error("fail exec method [getSelfOrg]. not support orderType:{}", type);
            return null;
        }

        log.info("success exec method [getHeaderOfWorkOrder]. workOrderId:{} result:{}", workOrderId, JsonUtils.objToString(rsp));
        return rsp;
    }


    /**
     * @param workOrderId
     * @param orgTypeFilter 0-查所有 1-查peer，2-查orderer  ,默认0
     * @return
     */
    public List<RspOrgOfWorkerOrderVO> listOrgByWorkOrderId(int workOrderId, byte orgTypeFilter) {
        log.info("start exec method [listOrgByWorkOrderId]. workOrderId:{} orgTypeFilter:{}", workOrderId, orgTypeFilter);
        TbWorkOrder tbWorkOrder = workOrderMapper.selectByPrimaryKey(workOrderId);
        if (Objects.isNull(tbWorkOrder)) return Collections.EMPTY_LIST;

        Byte type = tbWorkOrder.getType();
        List<RspOrgOfWorkerOrderVO> rspList = new ArrayList<>();
        if (type == OrderTypeEnum.NEW_CHAIN_FABRIC.getValue()) {
            ReqAddWorkOrderOfNewChainVO chainDetailVO = JsonUtils.stringToObj(tbWorkOrder.getDetail(), ReqAddWorkOrderOfNewChainVO.class);
            if (2 != orgTypeFilter) {
                chainDetailVO.getFabricPeerOrgList().stream()
                        .forEach(o -> {
                            RspOrgOfWorkerOrderVO rspOrg = new RspOrgOfWorkerOrderVO();
                            rspOrg.setOrgId(o.getOrgId());
                            rspOrg.setOrgName(orgMapper.selectNameByPrimaryKey(o.getOrgId()));
                            rspOrg.setBelongCompanyId(o.getCompanyId());
                            rspOrg.setBelongCompanyName(companyMapper.selectNameByPrimaryKey(o.getCompanyId()));
                            if (o.getCaNode() != null && o.getCaNode().getHostId() != null) {
                                rspOrg.setFabricCaHost(initHost(o.getCaNode().getHostId(), null));
                            }

                            Set<Integer> peerIdSet = o.getPeerNodeList().stream().map(order -> order.getHostId()).collect(Collectors.toSet());
                            rspOrg.setFabricPeerHosts(listHostFromIdSet(peerIdSet));
                            rspList.add(rspOrg);
                        });
            }

            if (1 != orgTypeFilter) {
                chainDetailVO.getFabricOrderOrgList().stream()
                        .forEach(o -> {
                            RspOrgOfWorkerOrderVO rspOrg = new RspOrgOfWorkerOrderVO();
                            rspOrg.setOrgId(o.getOrgId());
                            rspOrg.setOrgName(orgMapper.selectNameByPrimaryKey(o.getOrgId()));
                            rspOrg.setBelongCompanyId(o.getCompanyId());
                            rspOrg.setBelongCompanyName(companyMapper.selectNameByPrimaryKey(o.getCompanyId()));

                            Set<Integer> orderIdSet = o.getOrderNodeList().stream().map(order -> order.getHostId()).collect(Collectors.toSet());
                            rspOrg.setFabricOrderHosts(listHostFromIdSet(orderIdSet));
                            rspList.add(rspOrg);
                        });
            }

        } else if (type == OrderTypeEnum.NEW_CHAIN_FISCO.getValue()) {
            ReqAddWorkOrderOfNewChainVO chainDetailVO = JsonUtils.stringToObj(tbWorkOrder.getDetail(), ReqAddWorkOrderOfNewChainVO.class);
            chainDetailVO.getFiscoOrgList().stream()
                    .forEach(o -> {
                        RspOrgOfWorkerOrderVO rspOrg = new RspOrgOfWorkerOrderVO();
                        rspOrg.setOrgId(o.getOrgId());
                        rspOrg.setOrgName(orgMapper.selectNameByPrimaryKey(o.getOrgId()));
                        rspOrg.setBelongCompanyId(o.getCompanyId());
                        rspOrg.setBelongCompanyName(companyMapper.selectNameByPrimaryKey(o.getCompanyId()));
                        rspOrg.setFiscoHosts(listHostFromFiscoHostList(o.getFiscoHostList()));
                        rspList.add(rspOrg);
                    });
        } else if (type == OrderTypeEnum.NEW_APPLICATION_FABRIC.getValue() || type == OrderTypeEnum.NEW_APPLICATION_FISCO.getValue()) {
            ReqAddWorkOrderOfNewApplicationVO appDetail = JsonUtils.stringToObj(tbWorkOrder.getDetail(), ReqAddWorkOrderOfNewApplicationVO.class);
            appDetail.getConfirmOrgList().stream()
                    .forEach(o -> {
                        RspOrgOfWorkerOrderVO rspOrg = new RspOrgOfWorkerOrderVO();
                        rspOrg.setOrgId(o.getOrgId());
                        rspOrg.setOrgName(orgMapper.selectNameByPrimaryKey(o.getOrgId()));
                        rspOrg.setBelongCompanyId(o.getCompanyId());
                        rspOrg.setBelongCompanyName(companyMapper.selectNameByPrimaryKey(o.getCompanyId()));
                        rspList.add(rspOrg);
                    });
        }
        return rspList;
    }


    /**
     * @param workOrderId
     * @return
     */
    public List<RspHostVO> listHostFromWorkOrder(int workOrderId) {
        log.info("start exec method [listHostFromWorkOrder]. workOrderId:{}", workOrderId);
        TbWorkOrder tbWorkOrder = workOrderManager.verifyWorkOrderId(workOrderId);
        ReqAddWorkOrderOfNewChainVO chainDetailVO = JsonUtils.stringToObj(tbWorkOrder.getDetail(), ReqAddWorkOrderOfNewChainVO.class);
        Set<Integer> hostIdSet = workOrderManager.getAllHostIdFromWorkOrderDetail(chainDetailVO);
        if (CollectionUtil.isEmpty(hostIdSet)) {
            log.warn("fail exec method [listHostFromWorkOrder]. workOrderId:{} hostIdSet:{}", workOrderId, JsonUtils.objToString(hostIdSet));
            return Collections.EMPTY_LIST;
        }
        List<RspHostVO> rspHostVOList = new ArrayList<>();
        for (Integer hostId : hostIdSet) {
            TbHost tbHost = hostManager.verifyHostId(hostId);
            TbOrg tbOrg = orgManager.verifyOrgId(tbHost.getOrgId());
            RspHostVO hostVo = new RspHostVO();
            hostVo.setId(tbHost.getId());
            hostVo.setIp(tbHost.getIp());
            hostVo.setOrgId(tbOrg.getId());
            hostVo.setOrgName(tbOrg.getName());
            rspHostVOList.add(hostVo);
        }
        log.info("success exec method [listHostFromWorkOrder]. workOrderId:{} result:{}", workOrderId, JsonUtils.objToString(rspHostVOList));
        return rspHostVOList;
    }


    /**
     * @param hostIdSet
     * @return
     */
    private Set<RspOrgOfWorkerOrderVO.Host> listHostFromIdSet(Set<Integer> hostIdSet) {
        if (CollectionUtils.isEmpty(hostIdSet)) return null;
        Set<RspOrgOfWorkerOrderVO.Host> hostList = new HashSet<>();
        hostIdSet.stream().forEach(h -> hostList.add(initHost(h, null)));
        return hostList;
    }


    /**
     * @param fiscoHostList
     * @return
     */
    private List<RspOrgOfWorkerOrderVO.Host> listHostFromFiscoHostList(List<ReqAddWorkOrderOfNewChainVO.NewFiscoChainHost> fiscoHostList) {
        if (CollectionUtils.isEmpty(fiscoHostList)) return null;
        List<RspOrgOfWorkerOrderVO.Host> hostList = new ArrayList<>();
        fiscoHostList.stream().forEach(h -> hostList.add(initHost(h.getFiscoHostId(), h.getNodeCount())));
        return hostList;
    }


    /**
     * @param hostId
     * @param nodeCount
     * @return
     */
    private RspOrgOfWorkerOrderVO.Host initHost(int hostId, Integer nodeCount) {
        TbHost tbHost = hostManager.verifyHostId(hostId);
        RspOrgOfWorkerOrderVO.Host host = new RspOrgOfWorkerOrderVO.Host();
        host.setHostId(tbHost.getId());
        host.setHostIp(tbHost.getIp());
        host.setNodeCount(nodeCount);
        return host;
    }


    /**
     * @param platform
     * @param accountId
     * @param param
     */
    @Transactional
    public void closeWorkOrder(int platform, int accountId, ReqCloseOrderVO param) {
        log.info("start exec method [closeWorkOrder]. platform:{} accountId:{} param:{}", platform, accountId, JsonUtils.objToString(param));

        //check platform
        if (platform != PlatformEnum.DEV_OPS.getId()) {
            log.warn("fail exec method [closeWorkOrder]. current platform：{},but support", platform, PlatformEnum.DEV_OPS.getId());
            throw new WebServerException(ConstantCode.NOT_SUPPORT_OPERATE);
        }

        //check id
        TbWorkOrder tbWorkOrder = workOrderManager.verifyWorkOrderId(param.getId());

        //check before status
        List<Byte> status_list_support_permit = Arrays.asList(OrderStatusEnum.HANDLE_SUCCESS_BY_MANAGER.getValue());
        List<Byte> status_list_support_rejected = Arrays.asList(OrderStatusEnum.WAIT_FOR_MANAGER_HANDLE.getValue(), OrderStatusEnum.HANDLE_FAIL_BY_MANAGER.getValue());
        if (!SUPPORT_TARGET_STATUS_CLOSE_WORK_ORDER.contains(param.getOrderStatus())) {
            log.warn("fail exec method [closeWorkOrder]. inputStatus：{} ,but support:{}", param.getOrderStatus(), JsonUtils.objToString(SUPPORT_TARGET_STATUS_CLOSE_WORK_ORDER));
            throw new WebServerException(ConstantCode.NOT_SUPPORT_OPERATE);
        } else if (OrderStatusEnum.PERMIT_BY_MANAGER.getValue() == param.getOrderStatus() && !status_list_support_permit.contains(tbWorkOrder.getStatus())) {
            //一般而言，处理中的工单不允许结单，但是跨链工单比较特殊，处理中（所有组织完成添加之后）即可结单
            if (tbWorkOrder.getType() == OrderTypeEnum.JOIN_CROSS_CHAIN.getValue()) {
                log.info("try to finish cross order id:{}", tbWorkOrder.getId());
            } else {
                log.warn("fail exec method [closeWorkOrder] to permit workOrder. old status is:{},but support:{}", tbWorkOrder.getStatus(), JsonUtils.objToString(status_list_support_permit));
                throw new WebServerException(ConstantCode.NOT_SUPPORT_OPERATE);
            }
        } else if (OrderStatusEnum.REJECTED_BY_MANAGER.getValue() == param.getOrderStatus() && !status_list_support_rejected.contains(tbWorkOrder.getStatus())) {
            log.warn("fail exec method [closeWorkOrder] to reject workOrder. old status is:{},but support:{}", tbWorkOrder.getStatus(), JsonUtils.objToString(status_list_support_rejected));
            throw new WebServerException(ConstantCode.NOT_SUPPORT_OPERATE);
        }

        //update tb_work_order
        tbWorkOrder.setStatus(param.getOrderStatus());
        tbWorkOrder.setCloseByManager(accountId);
        tbWorkOrder.setGmtModified(new Date());
        tbWorkOrder.setDescription(param.getDescription());
        workOrderMapper.updateByPrimaryKeyWithBLOBs(tbWorkOrder);

        //update chain or app
        if (null != tbWorkOrder.getTargetId() && OrderStatusEnum.REJECTED_BY_MANAGER.getValue() == param.getOrderStatus()) {
            OrderStatusEnum orderStatusEnum = OrderStatusEnum.getByValue(param.getOrderStatus());
            ChainOrAppStatusEnum chainOrAppStatusEnum = orderStatusEnum.convertToChainOrAppStatusEnum();
            if (ORDER_TYPE_LIST_NEW_CHAIN.contains(tbWorkOrder.getType())) {
                TbChain tbChain = chainManager.verifyChainId(tbWorkOrder.getTargetId());
                tbChain.setCreateStatus(chainOrAppStatusEnum.getId());
                chainMapper.updateByPrimaryKey(tbChain);
            }
            if (ORDER_TYPE_LIST_NEW_APP.contains(tbWorkOrder.getType())) {
                TbApplication tbApplication = applicationManager.verifyApplicationId(tbWorkOrder.getTargetId());
                tbApplication.setCreateStatus(chainOrAppStatusEnum.getId());
                applicationMapper.updateByPrimaryKey(tbApplication);
            }
        }

        //send message
        workOrderMessageService.sendAfterManagerCloseWorkOrder(tbWorkOrder, OrderStatusEnum.getByValue(param.getOrderStatus()));
        log.info("success exec method [closeWorkOrder]. platform:{} accountId:{} param:{}", platform, accountId, JsonUtils.objToString(param));

    }


    /**
     * @param workOrderId
     * @param dockerImageType
     * @return
     */
    public RspNodeHostListInitVO initNodeHostList(int workOrderId, Byte dockerImageType) {
        log.info("start exec method [initNodeHostList]. workOrderId:{} dockerImageType:{}", workOrderId, dockerImageType);

        //check num of chain exists, status in ChainOrAppStatusEnum
        int numExists = chainMapper.getCountChainOfExist();
        log.info("check num of chain tag --> {}, now --> {}", limitConfig.getMaxChain(), numExists);
        if (numExists >= limitConfig.getMaxChain()) {
            throw new WebServerException(ConstantCode.UPPER_LIMIT_EXCEEDED);
        }

        //check workOrderId
        TbWorkOrder workOrder = workOrderManager.verifyWorkOrderId(workOrderId);
        //check completed status
        verifyAllOrgHasCompleted(workOrder);
        //check status
        if (!SUPPORT_STATUS_BEFORE_DEPLOY.contains(workOrder.getStatus())) {
            log.warn("fail exec method [initNodeHostList]. not support operation, currentStatus:{} support:{}", workOrder.getStatus(), JsonUtils.objToString(SUPPORT_STATUS_BEFORE_DEPLOY));
            throw new WebServerException(ConstantCode.NOT_SUPPORT_OPERATE);
        }

        //check workOrderId
        ReqAddWorkOrderOfNewChainVO chainDetail = JsonUtils.objToJavaBean(workOrder.getDetail(), ReqAddWorkOrderOfNewChainVO.class);

        //deploy init node host
        List<RespInitHost> respList = new ArrayList<>();
        if (workOrder.getType() == OrderTypeEnum.NEW_CHAIN_FISCO.getValue()) {
            respList = fiscoChainService.initNodeHostList(dockerImageType, chainDetail);
        }

        if (CollectionUtil.isEmpty(respList)) {
            log.warn("fail exec method [initNodeHostList].respList is null");
            throw new WebServerException(ConstantCode.HOST_CHECK_NOT_SUCCESS);
        }


        //result to view
        RspNodeHostListInitVO rspNodeHostListInitVO = new RspNodeHostListInitVO();
        rspNodeHostListInitVO.setSuccess(respList.stream().filter(host -> !host.getSuccess()).count() == 0);
        List<RspNodeHostListInitVO.Host> hostList = new ArrayList<>();
        rspNodeHostListInitVO.setHostList(hostList);

        for (RespInitHost respInitHost : respList) {
            TbHost tbHost = hostManager.verifyHostId(respInitHost.getHostId());
            TbOrg tbOrg = orgManager.verifyOrgId(tbHost.getOrgId());
            RspNodeHostListInitVO.Host hostVo = new RspNodeHostListInitVO.Host();
            hostVo.setSuccess(respInitHost.getSuccess());
            hostVo.setHostId(tbHost.getId());
            hostVo.setOrgId(tbOrg.getId());
            hostVo.setOrgName(tbOrg.getName());
            hostVo.setErrorMessage(respInitHost.getErrorMessage());
            hostList.add(hostVo);
        }

        log.info("finish exec method [initNodeHostList]. result:{}", JsonUtils.objToString(rspNodeHostListInitVO));
        return rspNodeHostListInitVO;
    }


    /**
     * delete by workOrderId.
     *
     * @param workOrderId
     */
    @Transactional
    public void deleteByWorkOrderId(String account, int workOrderId) {
        log.info("start exec method [deleteByWorkOrderId]. account:{} workOrderId:{}", account, workOrderId);
        TbWorkOrder tbWorkOrder = workOrderManager.verifyWorkOrderId(workOrderId);

        if (!SUPPORT_DELETE_WORK_ORDER.contains(tbWorkOrder.getStatus()) && !config.getOpenDebug()) {
            log.warn("start exec method [deleteByWorkOrderId]. status is not in fail， status:{} isOpenDebug:{}", tbWorkOrder.getStatus(), config.getOpenDebug());
            throw new WebServerException(ConstantCode.NOT_SUPPORT_OPERATE);
        }

        if (Constant.ORDER_TYPE_LIST_NEW_CHAIN.contains(tbWorkOrder.getType()) && tbWorkOrder.getTargetId() != null) {
            log.info("orderType:{} targetId:{} workOrderId:{}", tbWorkOrder.getType(), tbWorkOrder.getTargetId(), workOrderId);
            deleteChainService.deleteByChainId(account, tbWorkOrder.getTargetId());
        } else if (Constant.ORDER_TYPE_LIST_NEW_CHAIN.contains(tbWorkOrder.getType())) {
            workOrderMapper.deleteByPrimaryKey(workOrderId);
        } else {
            throw new WebServerException(ConstantCode.NOT_SUPPORT_OPERATE);
        }
        //TODO application


        log.info("success exec method [deleteByWorkOrderId]. workOrderId:{}", workOrderId);
    }


    public boolean isCrossChainNameUsed(ReqAddWorkOrderOfCrossChainVO param) {
        log.debug("fix isCrossChainNameUsed-->" + param.getCrossChainName());
        List<TbWorkOrder> list = workOrderMapper.listByNameOfCrossChain(param.getCrossChainName());
        if (list == null || list.size() < 1) {
            return false;
        }
        return true;
    }

    @Transactional
    public RspAddWorkOrderOfCrossChainVO addWorkOrderOfCrossChain(byte platform, int currentAccountId, String currentAccountName, ReqAddWorkOrderOfCrossChainVO param) {
        log.info("start exec method [addWorkOrderOfCrossChain]. platform:{} currentAccountId:{} currentAccountName:{} param:{}", platform, currentAccountId, currentAccountName, JsonUtils.objToString(param));

        //check
//        requireDeveloperOwnApplicationIdList(currentAccountId, param.getApplicationIdCollection());


        param.getApplicationOrgCollection().stream().forEach(o -> o.setCompanyId(orgManager.getCompanyIdByOrgId(o.getOrgId())));

        //add work order
        WorkOrderVO workOrderParam = new WorkOrderVO();
        workOrderParam.setDetail(JsonUtils.objToString(param));
        workOrderParam.setOrderStatus(OrderStatusEnum.APPLYING_BY_DEVELOPER);
        workOrderParam.setOrderType(OrderTypeEnum.JOIN_CROSS_CHAIN);
        workOrderParam.setCreateFromPlatform(PlatformEnum.getById(platform));
        workOrderParam.setCreateBy(currentAccountId);
        workOrderParam.setModifyBy(currentAccountId);
        TbWorkOrder workOrderOnDb = addWorkOrder(workOrderParam);

        //send message
//        workOrderMessageService.sendAfterDeveloperAddWorkOrder(workOrderOnDb);


        //check complete status
        if (hasAllOrgCompleted(workOrderOnDb)) {
            workOrderOnDb.setStatus(OrderStatusEnum.WAIT_FOR_MANAGER_HANDLE.getValue());
            updateWorkOrder(workOrderOnDb);
        } else if (PlatformEnum.DEVELOPER.getId() != platform) {
            throw new WebServerException(ConstantCode.INCOMPLETE_PARAM);
        }


        RspAddWorkOrderOfCrossChainVO rspAddWorkOrderOfCrossChainVO = new RspAddWorkOrderOfCrossChainVO();
        rspAddWorkOrderOfCrossChainVO.setWorkOrderId(workOrderOnDb.getId());

        log.info("success exec method [addWorkOrderOfCrossChain].");
        return rspAddWorkOrderOfCrossChainVO;
    }


    /**
     * @param developerAccountId
     * @return
     */
    private Set<Integer> getAllApplicationIdByDeveloperAccountId(int developerAccountId) {
        log.info("start exec method [getAllApplicationIdByDeveloperAccountId]. developerAccountId:{}", JsonUtils.objToString(developerAccountId));

        int companyId = companyDeveloperService.selectCompanyIdByDeveloperId(developerAccountId);
        List<Integer> companyIdList = Arrays.asList(companyId);
        //get set of chainId
        Set<Integer> chainIdIdSet = applicationManager.getApplicationIdSetByCompanyIdList(companyIdList);

        log.info("finish exec method [getAllApplicationIdByDeveloperAccountId]. developerAccountId:{} result:{}", JsonUtils.objToString(developerAccountId), JsonUtils.objToString(chainIdIdSet));
        return chainIdIdSet;
    }


    /**
     * @param platform
     * @param currentAccountId
     * @param currentAccountName
     * @param param
     */
    public void updateWorkOrderOfCrossChain(byte platform, int currentAccountId, String currentAccountName,
                                            ReqUpdateWorkOrderOfCrossChainVO param) {
        log.info("start exec method [updateWorkOrderOfCrossChain]. platform:{} currentAccountId:{} currentAccountName:{} param:{}", platform, currentAccountId, currentAccountName, JsonUtils.objToString(param));
        //check workOrderId
        TbWorkOrder workOrder = workOrderManager.verifyWorkOrderId(param.getWorkOrderId());
        if (OrderTypeEnum.JOIN_CROSS_CHAIN.getValue() != workOrder.getType()) {
            log.warn("fail exec method [updateWorkOrderOfNewChain]. not support orderType:{}", workOrder.getType());
            throw new WebServerException(ConstantCode.NOT_SUPPORT_OPERATE);
        }

        ReqAddWorkOrderOfCrossChainVO crossChainDetail = JsonUtils.objToJavaBean(workOrder.getDetail(), ReqAddWorkOrderOfCrossChainVO.class);

        //set confirm result
        Integer currentCompanyId = companyDeveloperService.selectCompanyIdByDeveloperId(currentAccountId);
        ReqAddWorkOrderOfCrossChainVO.ApplicationOfOrg inputOrg = param.getApplicationOfOrg();
        if (!currentCompanyId.equals(inputOrg.getCompanyId())) {
            log.warn("fail exec method [updateWorkOrderOfNewChain]. currentCompanyId:{} but input:{}", currentCompanyId, inputOrg.getCompanyId());
            throw new WebServerException(ConstantCode.INCOMPLETE_PARAM.attach("invalid companyId"));
        }

        log.info("fix cross handler-->inputOrg-->{}", JsonUtils.objToString(inputOrg));
        crossChainDetail.getApplicationOrgCollection().stream()
                .filter(oldOrg -> oldOrg.getOrgId().equals(inputOrg.getOrgId()))
                .findFirst().ifPresent(dbOrg -> {
            BeanUtils.copyProperties(inputOrg, dbOrg);
        });
        log.info("fix cross handler-->crossChainDetail-->{}", JsonUtils.objToString(crossChainDetail));

        //update workOrder
        workOrder.setDetail(JsonUtils.objToString(crossChainDetail));
        workOrder.setModifyBy(currentAccountId);
        if (hasAllOrgCompleted(workOrder)) {
            workOrder.setStatus(OrderStatusEnum.WAIT_FOR_MANAGER_HANDLE.getValue());
        }

        updateWorkOrder(workOrder);

        //send message
//        workOrderMessageService.sendAfterDeveloperUpdateWorkOrder(currentAccountId, currentAccountName, workOrder);
        log.info("success exec method [updateWorkOrderOfNewApplication].");
    }
}
